#!/usr/bin/env python2
#-*- coding: utf-8 -*-

import os
import sys
import copy
import re
import paramiko

from subprocess import Popen, PIPE
from socket import error as socket_error
from optparse import OptionParser, Option
from config import Config
from utils import _exec_shell, _put_remote, _exec_remote2, _str2dict

class UmpException(Exception):
    pass


class LichFault(UmpException):
    pass

# def vid2eid(volume_id):
#     volume = db_api.volume_get(volume_id)
#     eid = volume.ccid
#     return eid


def add(x, y):
    return x + y 


def args(*args, **kwargs):
    def _decorator(func):
        func.__dict__.setdefault('options', []).insert(0, (args, kwargs))
        return func
    return _decorator


class IOPSManager(object):
    
    def __init__(self):
        #self.hostnames = self.get_all_node
        self.config = Config()
        self.home = self.config.home

    @args('--hostname', dest="hostname")
    @args('--cmd', dest="cmd")
    def _exec_remote(self, hostname, cmd, password=None, username='root'):
        stdout, stderr, recode = _exec_remote2(hostname, cmd, password=password, user=username)

        if stdout.find('No license found.') != -1:
            return "No license found."
        elif stdout.find('Invalid license.') != -1:
            return "Invalid license."
        elif stdout.find('License expired.') != -1:
            return "License expired."
        elif stdout.find('Excess capacity.') != -1:
            return "Excess capacity."   
        if recode not in ['0', 0]:
            errors = outerr.split('\n')
            errors = [error for error in errors if error.startswith('utils.Exp')]
            error = ''.join(errors)
            if errors == []:
                error = outerr
            raise LichFault('LichFault(%s)'%error)
        return stdout

    def get_all_node(self):
        cmd = '/opt/fusionstack/lich/admin/cluster.py listnode'
        res = os.popen(cmd).read()
        res_list = res.split('\n')
        names = [res.split(':')[3] for res in res_list if res]
        hostnames = [n for n in names]
        return set(hostnames)

    def _get_remote(self,hostname,local_dir,remote_dir,username='root',password='mdsmds'):
        '''download from remote host'''
        port = 22
        s = paramiko.SSHClient()
        s.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        try:
            s.connect(hostname, port, username, password, timeout = 1)
            sftp = s.open_sftp()
            files=sftp.listdir(remote_dir)
            for f in files:
                sftp.get(os.path.join(remote_dir,f),os.path.join(local_dir,f))
            sftp.close()
        except Exception,e:
            print "connect error:%s!"%e

    @args('--host', dest="host")
    def host_iops(self,host):
        local_dir = os.path.join(self.config.shm, 'host/%s'%host)
        if not os.path.exists(local_dir):
            p1 = Popen(['mkdir','-p',local_dir], stdout=PIPE)
            stdout, stderr = p1.communicate()
            if not p1.returncode == 0:
                print >>sys.stderr, 'Warn: create file error', stderr

        remote_dir = os.path.join(self.config.shm, 'volume/io')
        self._get_remote(host, local_dir, remote_dir)

    @args('--host', dest="host")
    def get_hostname(self, host):
        cmd = 'hostname'
        res = self._exec_remote(host, cmd)
        return res

    def is_update_key(self):
        uuid,ip,vswitch,hostname = self.config.unique_key(self.home)
        if not uuid:
            return True 
        if vswitch is not None and not vswitch:
            return True
        else:
            return False 

    def get_network(self):
        res = self.config.network()[0][0]
        return res

    @args('--host', dest="host")
    @args('--password', dest="password")
    def etc_hosts(self, host, password):
        cmd = '/opt/fusionstack/lich/admin/cluster.py configdump'
        res = self._exec_remote(host, cmd, password=password)
        mode = _str2dict(res)
        
        cmd = 'cat /etc/hosts'
        if mode['globals.nohosts'] == 'on':
            cmd = 'cat /opt/fusionstack/etc/hosts.conf'
        res = self._exec_remote(host, cmd, password=password)
        return res
    
    
    @args('--ip', dest="ip")
    def node_date(self, ip):
        cmd = 'date +%s'
        res = self._exec_remote(ip, cmd)
        return res    
    
    
    @args('--ip', dest="ip")
    def storage_create(self, ip):
        cmd = 'lich.storage --stat /'
        res = self._exec_remote(ip, cmd)
        return res
    

    @args('--ip', dest="ip")
    def node_invalid(self, ip):
        cmd = '%s/lich/libexec/lich.license -m list'%self.home
        res = self._exec_remote(ip, cmd)
        return res
    
  
    @args('--ip', dest="ip")
    def node_register_info(self, ip):
        cmd = '%s/lich/libexec/lich.license -m sniffer'%self.home
        res = self._exec_remote(ip, cmd)
        return res
    
    
    @args('--host', dest="host")
    @args('--ip', dest="ip")
    @args('--connected', dest="connected")
    def node_regist(self, host, ip, connected):
        cmd = 'scp /tmp/fusionstack_license/%s root@%s:/opt/fusionstack/license'%(host, ip)
        local = '/tmp/fusionstack_license/%s'%host
        remote = '/opt/fusionstack/license'
        _put_remote(ip, local, remote) 
#        res = self._exec_remote(connected, cmd)
        return ''    
    
    
    @args('--ip', dest="ip")
    def license_file_date(self, ip):
        cmd = '''res=$(ls --full-time /opt/fusionstack/license 2>/dev/null); if [ $? -eq 0 ]; then echo $res; else echo "error"; fi'''
        res = self._exec_remote(ip, cmd)
        return res    
    
    @args('--port', dest="port")
    def iscsi_port(self, port):
        new_config = []
        with open("%s/etc/lich.conf"%self.home, 'r') as f:
            is_iscsi = False
            row = 3
            for index,i in enumerate(f):
                if i.find('iscsi_port') != -1:
                    is_iscsi = True
                    try:
                        old_port = i.strip().split(' ')[-1].replace(';', '')
                    except:
                        return 'error'
                    if port == '':
                        return 'error'
                    else:	
                        if i.find('#iscsi_port') != -1:
                            i = i.replace('#iscsi_port', 'iscsi_port')
                        i = i.replace(old_port, port)
                new_config.append(i)

                if i.find('iscsi_port') != -1:
                    row = index + 2 
            if not is_iscsi :
                old_port = 3260
                if port != 'check':
                    new_config.insert(row,'        iscsi_port %s;\n'%port)
                else:
                    new_config.insert(row,'        iscsi_port %s;\n'%old_port)
                    
            if port == 'check':
                return old_port 

        print ' %s/etc/lich.conf.org'%self.home
        os.system('rm %s/etc/lich.conf.org 2>/dev/null'%self.home)
        with open("%s/etc/lich.conf.org"%self.home, 'w') as wf:
            [wf.write(i) for i in new_config]
        #cp, lich.conf can not support mv
        os.system('cp %s/etc/lich.conf.org %s/etc/lich.conf'%(self.home, self.home))
        cmd ='/opt/fusionstack/lich/admin/cluster.py update etc'
        _exec_shell(cmd)
        return 'success'
    
    @args('--name', dest="name")
    def clustername(self, name):
        new_config = []
        with open("%s/etc/lich.conf"%self.home, 'r') as f:
            is_iscsi = False
            row = 3
            for index,i in enumerate(f):
                if i.strip().startswith('#clustername') or i.strip().startswith('clustername'):
                    is_iscsi = True
		    try:
                        old_port = i.strip().split(' ')[-1].replace(';', '')
                    except:
                        return 'error'
                    if name == '':
                        return 'error'
                    else:	
                        i = '        clustername %s;\n'%name
                new_config.append(i)

                if i.startswith('#clustername') or i.startswith('clustername'):
                    row = index + 2 

            if not is_iscsi :
            	new_config.insert(row,'        clustername %s;\n'%name)

        print ' %s/etc/lich.conf.org'%self.home
        os.system('rm %s/etc/lich.conf.org 2>/dev/null'%self.home)
        with open("%s/etc/lich.conf.org"%self.home, 'w') as wf:
            [wf.write(i) for i in new_config]
        #cp, lich.conf can not support mv
        os.system('cp %s/etc/lich.conf.org %s/etc/lich.conf'%(self.home, self.home))
        #cmd ='/opt/fusionstack/lich/admin/cluster.py update etc'
        #_exec_shell(cmd)
        return 'success'

    @args('--iqn', dest="iqn")
    def iqn(self, iqn):
        new_config = []
        with open("%s/etc/lich.conf"%self.home, 'r') as f:
            is_iscsi = False
            row = 3
            for index,i in enumerate(f):
                if i.strip().startswith('#iqn') or i.strip().startswith('iqn'):
                    is_iscsi = True
		    try:
                        old_port = i.strip().split(' ')[-1].replace(';', '')
                    except:
                        return 'error'
                    if iqn == '':
                        return 'error'
                    else:	
                        i = '        iqn %s;\n'%iqn
                new_config.append(i)

                if i.startswith('#iqn') or i.startswith('iqn'):
                    row = index + 2 

            if not is_iscsi :
            	new_config.insert(row,'        iqn %s;\n'%iqn)

        print ' %s/etc/lich.conf.org'%self.home
        os.system('rm %s/etc/lich.conf.org 2>/dev/null'%self.home)
        with open("%s/etc/lich.conf.org"%self.home, 'w') as wf:
            [wf.write(i) for i in new_config]
        #cp, lich.conf can not support mv
        os.system('cp %s/etc/lich.conf.org %s/etc/lich.conf'%(self.home, self.home))
        #cmd ='/opt/fusionstack/lich/admin/cluster.py update etc'
        #_exec_shell(cmd)
        return 'success'

    @args('--setip', dest="setip")
    @args('--ip', dest="ip")
    @args('--check', dest="check")
    def dhost_config(self, setip, ip,check):
        new_config = []
        with open("%s/etc/lich.conf"%self.home, 'r') as f:
            for i in f:
                if i.find('arbitor') != -1:
                    try:
                        arbitor = i.strip().split(' ')[-1].replace(';', '')
                    except:
                        return 'error'
                    if setip == 'noset':
                        if i.find('#arbitor') == -1:
                            return arbitor
                        else:
                            return 'error'
                    elif  setip == '':
                        if i.find('#arbitor') == -1:
	                    if i.find('arbitor') != -1:
        	                i = i.replace('arbitor', '#arbitor')
                    else:
                        net_ip = self.config.network()[0][0].split('.')[0:3]
                        if net_ip != setip.split('.')[0:3]:
                            return 'unerror'
                        if os.system('ping -c 2 %s'%setip) != 0:
                            return 'ping_error'                        
                        if i.find('#arbitor') != -1:
                            i = i.replace('#arbitor', 'arbitor')
                        i = i.replace(arbitor, setip)
                if setip != 'noset':
                    new_config.append(i)
        if setip != 'noset':
            ips = ip.split('+')
            if setip in ips:
                return 'same_ip'

            if check == 'check':
                return 'success'
            print ' %s/etc/lich.conf.org'%self.home
            os.system('rm %s/etc/lich.conf.org 2>/dev/null'%self.home)
            with open("%s/etc/lich.conf.org"%self.home, 'w') as wf:
                [wf.write(i) for i in new_config]
            #cp, lich.conf can not support mv
            os.system('cp %s/etc/lich.conf.org %s/etc/lich.conf'%(self.home, self.home))
            cmd ='/opt/fusionstack/lich/admin/cluster.py update etc'
            _exec_shell(cmd)
#            [os.system('scp %s/etc/lich.conf root@%s:%s/etc/lich.conf'%(self.home, i, self.home)) for i in ips]
            return 'success'
    
    
def match(name, key_value_tuples):
    for (k, v) in key_value_tuples:
        if k.lower() == name:
            return v
    sys.exit(2)


def methods_of(obj):
    """Get all callable methods of an object that 
    don't start with underscore
    returns a list of tuples of the form (method_name, method)"""
    result = []
    for i in dir(obj):
        if callable(getattr(obj, i)) and not i.startswith('_'):
            result.append((i, getattr(obj, i)))
    return result


CATEGORIES = [
        ('node',IOPSManager),
        ]


class MyOption(Option):
    ACTIONS = Option.ACTIONS + ("extend",)
    STORE_ACTIONS = Option.STORE_ACTIONS + ("extend",)
    TYPED_ACTIONS = Option.TYPED_ACTIONS + ("extend",)
    ALWAYS_TYPED_ACTIONS = Option.ALWAYS_TYPED_ACTIONS + ("extend",)
    
    def take_action(self, action, dest, opt, value, values, parser):
        if action == "extend":
            lvalue = value.split(" ")
            values.ensure_value(dest, []).extend(lvalue)
        else:
            Option.take_action(self,
                     action, dest, opt, value, values, parser)

def main():
    argv = copy.copy(sys.argv)
    script_name = argv.pop(0)
    category = 'node'
    command_object = match(category, CATEGORIES)()
    actions = methods_of(command_object)
    fn = ''
    if len(argv) < 1:
#         print  hasattr(command_object, '__call__')
        if hasattr(command_object, '__call__'):
            action = ''
            fn = command_object.__call__
        else:
            print script_name + " category action [<args>]"
            print "Available actions for %s category:" % category
            for k, _v in actions:
                print "\t%s" % k
            sys.exit(2)
    else:
        action = argv.pop(0)
        fn = getattr(command_object, action)

    options = getattr(fn, 'options', [])

    usage = "%%prog %s %s <args> [options]" % (category, action)
    parser = OptionParser(usage=usage, option_class=MyOption)
    for ar, kw in options:
        parser.add_option(*ar, **kw)

    (opts, fn_args) = parser.parse_args(argv)
    fn_kwargs = vars(opts)
    for k, v in fn_kwargs.items():
        if v is None:
            del fn_kwargs[k]
    try:
        res = fn(*fn_args, **fn_kwargs)
        return res
        sys.exit(0)
    except TypeError:
        print "Possible wrong number of arguments supplied"
        print fn.__doc__
        parser.print_help()
        raise
    except Exception:
        print "Command failed, please check log for more info"
        raise

                              

if __name__ == '__main__':
    result = main()
    if result is not None:
        print result

